package com.ptms.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;

/**
 * JWT 工具类：负责 Token 生成、解析、验证，补充日志便于调试
 */
@Component
public class JwtUtil {

    // 从配置文件读取 BASE64 编码的密钥（必须是标准 Base64 格式，无下划线等非法字符）
    @Value("${ptms.jwt.secret}")
    private String base64Secret;

    // 从配置文件读取有效期（单位：毫秒，建议配置为 86400000 即 24 小时）
    @Value("${ptms.jwt.expire}")
    private long expire;

    /**
     * 初始化日志：项目启动时打印 JwtUtil 加载状态（确认配置生效）
     */
    public JwtUtil() {
        System.out.println("=== JwtUtil 实例已创建，等待配置注入...");
    }

    /**
     * 核心修复：将 Base64 密钥解码为 JJWT 支持的 Key 对象
     * 避免直接使用原始字符串，解决 "Illegal base64 character" 错误
     */
    private Key getSigningKey() {
        // 1. 校验 Base64 密钥是否为空
        if (!StringUtils.hasText(base64Secret)) {
            throw new IllegalArgumentException("JWT 密钥不能为空，请在配置文件中设置 ptms.jwt.secret");
        }
        try {
            // 2. 解码 Base64 密钥（标准 Base64 不允许下划线、空格等字符）
            byte[] keyBytes = Base64.getDecoder().decode(base64Secret);
            // 3. 生成 HS512 算法对应的 Key（HS512 要求密钥至少 512 位=64 字节，此处自动校验）
            Key signingKey = Keys.hmacShaKeyFor(keyBytes);
            // 日志：密钥解码成功（仅打印密钥长度，避免暴露密钥内容）
            System.out.println("=== JWT 密钥解码成功！密钥长度：" + keyBytes.length + " 字节（HS512 要求≥64字节）");
            return signingKey;
        } catch (IllegalArgumentException e) {
            // 捕获 Base64 解码异常（如密钥含非法字符、格式错误）
            System.err.println("=== JWT 密钥解码失败！原因：" + e.getMessage());
            throw new RuntimeException("JWT 密钥格式错误：必须是标准 Base64 编码字符串", e);
        }
    }

    /**
     * 生成 Token（根据用户名）
     * @param username 用户名（作为 Token 主题）
     * @return 合法的 JWT Token
     */
    public String generateToken(String username) {
        // 校验用户名不为空
        if (!StringUtils.hasText(username)) {
            System.err.println("=== 生成 JWT Token 失败：用户名为空");
            throw new IllegalArgumentException("生成 JWT Token 失败：用户名为空");
        }

        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + expire);
        // 计算有效期（转换为小时，便于日志阅读）
        long expireHours = expire / 1000 / 3600;

        try {
            String token = Jwts.builder()
                    .setSubject(username)  // Token 主题（存储用户名，后续可解析获取）
                    .setIssuedAt(now)      // 签发时间（便于后续判断 Token 是否提前生成）
                    .setExpiration(expirationDate)  // 过期时间
                    .signWith(getSigningKey(), SignatureAlgorithm.HS512)  // 使用解码后的 Key 签名
                    .compact();  // 生成最终 Token

            // 日志：Token 生成成功（仅打印前20位，避免日志过长）
            System.out.println("=== 生成 JWT Token 成功！用户名：" + username + "，有效期：" + expireHours + "小时，Token（前20位）：" + token.substring(0, 20) + "...");
            return token;
        } catch (Exception e) {
            System.err.println("=== 生成 JWT Token 失败！用户名：" + username + "，原因：" + e.getMessage());
            throw new RuntimeException("生成 JWT Token 失败", e);
        }
    }

    /**
     * 从 Token 中获取用户名（主题）
     * @param token JWT Token
     * @return 用户名
     * @throws RuntimeException Token 无效或解析失败时抛出
     */
    public String getUsernameFromToken(String token) {
        // 1. 校验 Token 不为空
        if (!StringUtils.hasText(token)) {
            System.err.println("=== 解析 JWT Token 失败：Token 为空");
            throw new RuntimeException("解析 JWT Token 失败：Token 为空");
        }

        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())  // 使用解码后的 Key 解析
                    .build()
                    .parseClaimsJws(token)  // 解析 Token（自动校验签名和格式）
                    .getBody();

            // 2. 获取 Token 主题（用户名）并校验不为空
            String username = claims.getSubject();
            if (!StringUtils.hasText(username)) {
                System.err.println("=== 解析 JWT Token 失败：Token 中用户名为空");
                throw new RuntimeException("JWT Token 中用户名为空");
            }

            // 日志：Token 解析成功，获取用户名
            System.out.println("=== 解析 JWT Token 成功！用户名：" + username + "，Token（前20位）：" + token.substring(0, 20) + "...");
            return username;
        } catch (ExpiredJwtException e) {
            System.err.println("=== 解析 JWT Token 失败：Token 已过期，Token（前20位）：" + token.substring(0, 20) + "...");
            throw new RuntimeException("JWT Token 已过期", e);
        } catch (MalformedJwtException e) {
            System.err.println("=== 解析 JWT Token 失败：Token 格式错误，Token（前20位）：" + token.substring(0, 20) + "...");
            throw new RuntimeException("JWT Token 格式错误（如非法字符、结构异常）", e);
        } catch (SignatureException e) {
            System.err.println("=== 解析 JWT Token 失败：签名错误（密钥不匹配或 Token 被篡改），Token（前20位）：" + token.substring(0, 20) + "...");
            throw new RuntimeException("JWT Token 签名错误（密钥不匹配或 Token 被篡改）", e);
        } catch (IllegalArgumentException e) {
            System.err.println("=== 解析 JWT Token 失败：参数异常，Token（前20位）：" + token.substring(0, 20) + "...");
            throw new RuntimeException("JWT Token 解析失败：参数异常", e);
        }
    }

    /**
     * 验证 Token 是否有效（未过期+签名正确+格式合法）
     * @param token JWT Token
     * @return true=有效，false=无效
     */
    public boolean validateToken(String token) {
        try {
            // 调用解析方法，若无异常则 Token 有效
            getUsernameFromToken(token);
            System.out.println("=== JWT Token 验证通过！Token（前20位）：" + token.substring(0, 20) + "...");
            return true;
        } catch (Exception e) {
            // 捕获所有解析异常，返回无效
            System.err.println("=== JWT Token 验证失败！原因：" + e.getMessage());
            return false;
        }
    }

    /**
     * （可选）获取 Token 剩余有效期（单位：毫秒）
     * @param token JWT Token
     * @return 剩余毫秒数（负数表示已过期）
     */
    public long getRemainingTime(String token) {
        if (!StringUtils.hasText(token)) {
            System.err.println("=== 获取 Token 剩余有效期失败：Token 为空");
            return -1;
        }

        try {
            Claims claims = Jwts.parserBuilder()
                    .setSigningKey(getSigningKey())
                    .build()
                    .parseClaimsJws(token)
                    .getBody();

            Date expirationDate = claims.getExpiration();
            Date now = new Date();
            long remainingTime = expirationDate.getTime() - now.getTime();
            // 日志：打印剩余有效期（转换为分钟，便于阅读）
            System.out.println("=== 获取 Token 剩余有效期成功！剩余时间：" + (remainingTime / 1000 / 60) + " 分钟，Token（前20位）：" + token.substring(0, 20) + "...");
            return remainingTime;
        } catch (Exception e) {
            System.err.println("=== 获取 Token 剩余有效期失败！原因：" + e.getMessage());
            return -1;
        }
    }
}